package cn.iocoder.yudao.module.ad.plan.service;

import cn.iocoder.yudao.framework.business.basic.dao.query.CoreQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.pojo.vo.UserSimpleVO;
import cn.iocoder.yudao.framework.business.basic.service.CrudService;
import cn.iocoder.yudao.framework.business.basic.service.impl.BaseService;
import cn.iocoder.yudao.framework.business.order.pojo.vo.OrderSubmitVO;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ad.effect.dal.mysql.AdEffectRecordMapper;
import cn.iocoder.yudao.module.ad.order.service.AppAdPlanOrderService;
import cn.iocoder.yudao.module.ad.order.vo.AdPlanOrderSubmitDTO;
import cn.iocoder.yudao.module.ad.plan.dal.dataobject.AdPlanDO;
import cn.iocoder.yudao.module.ad.plan.dal.mysql.AdPlanMapper;
import cn.iocoder.yudao.module.ad.plan.vo.AdPlanQueryDTO;
import cn.iocoder.yudao.module.ad.plan.vo.AdPlanSaveDTO;
import cn.iocoder.yudao.module.ad.plan.vo.AppAdPlanVO;
import cn.iocoder.yudao.module.api.ad.plan.dto.AdPlanRespDTO;
import cn.iocoder.yudao.module.api.ad.plan.enums.AdPlanBillingTypeEnum;
import cn.iocoder.yudao.module.api.ad.plan.enums.AdPlanExceptTypeEnum;
import cn.iocoder.yudao.module.api.ad.plan.enums.AdPlanStatusEnum;
import cn.iocoder.yudao.module.api.infrastructure.config.ConfigApiV2;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.AdConfigRespDTO;
import cn.iocoder.yudao.module.api.member.user.MemberUserApi;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.Set;

import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_AD;

/**
 * 广告投放计划 Service 实现类
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Service
@Validated
public class AppAdPlanServiceImpl extends BaseService<AdPlanDO, AppAdPlanVO, AdPlanQueryDTO> implements AppAdPlanService, CrudService<AdPlanDO, AppAdPlanVO, AdPlanSaveDTO, AdPlanQueryDTO> {

    @Resource
    private AdPlanMapper adPlanMapper;
    @Resource
    private AdEffectRecordMapper effectRecordMapper;

    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private AppAdPlanOrderService appAdPlanOrderService;

    @Resource
    private ConfigApiV2 configApiV2;
    @Resource
    private MemberUserApi memberUserApi;

    @Override
    public AdPlanMapper getInjectedMapper() {
        return adPlanMapper;
    }

    @Override
    public AdPlanDO convertToEntity(AdPlanSaveDTO param) {
        return BeanUtils.toBean(param, AdPlanDO.class);
    }

    @Nonnull
    @Override
    protected CoreQueryWrapper<AdPlanDO> buildQueryWrapper(AdPlanQueryDTO param) {
        CoreQueryWrapper<AdPlanDO> wrapper = super.buildQueryWrapper(param);
        wrapper.eqIfPresent("status", param.getStatus());
        return wrapper;
    }

    @Override
    public void beforeCreate(AdPlanDO entity, Long userId) {
        UserSimpleVO user = memberUserApi.getUser(userId);
        entity.setAdvertiser(user.getNickname());
        entity.setExceptType(AdPlanExceptTypeEnum.DURATION.getValue());
        AdConfigRespDTO config = configApiV2.getConfig(CONFIG_AD, AdConfigRespDTO.class);
        Assertions.isTrue(entity.getBudgetPrice() >= config.getMinBudgetAmount(), "投放预算不能低于" + MoneyUtils.fenToYuanStr(config.getMinBudgetAmount()) + "元");
        entity.setBillingType(AdPlanBillingTypeEnum.PLAY.getValue());
        entity.setBillingUnitPrice(config.getPlayUnitPrice());
        entity.setExceptExposureCount(entity.getBudgetPrice() / config.getExposureUnitPrice());
        entity.setExceptPlayCount(entity.getBudgetPrice() / config.getPlayUnitPrice());
        entity.setStatus(AdPlanStatusEnum.PAY.getValue());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrderSubmitVO submit(AdPlanSaveDTO param, Long userId) {
        Long id = create(param, userId);
        AdPlanOrderSubmitDTO submitVO = new AdPlanOrderSubmitDTO();
        submitVO.setPlanId(id);
        return appAdPlanOrderService.submit(submitVO, userId);
    }

    @Override
    public void updateWhenPaid(String tradeNo, Long userId, Long planId) {
        AdPlanDO entity = getInjectedMapper().selectById(planId);
        if (entity == null) {
            return;
        }
        AdPlanDO updateObj = new AdPlanDO();
        updateObj.setId(entity.getId());
        updateObj.setStatus(AdPlanStatusEnum.AUDIT.getValue());
        getInjectedMapper().updateById(updateObj);
    }

    @Override
    public AdPlanRespDTO takeAvailable(Set<Long> excludePlanIds) {
        AdPlanDO adPlanDO = getInjectedMapper().selectAvailableByRandom(excludePlanIds);
        return BeanUtils.toBean(adPlanDO, AdPlanRespDTO.class);
    }

    @Override
    public AdPlanRespDTO takeAvailable(Long excludeUserId, Set<Long> excludePlanIds) {
        AdPlanDO adPlanDO = getInjectedMapper().selectAvailableByRandom(excludeUserId, excludePlanIds);
        return BeanUtils.toBean(adPlanDO, AdPlanRespDTO.class);
    }

    @Override
    public Integer updateSpendPriceWhenPlayComplete(Long planId) {
        AdPlanDO entity = getInjectedMapper().selectById(planId);
        if (entity == null) {
            return null;
        }
        int exposureCount = effectRecordMapper.selectExposureCountByPlanId(planId);
        AdPlanDO updateObj = new AdPlanDO();
        updateObj.setId(entity.getId());
        updateObj.setSpendPrice(entity.getSpendPrice() + entity.getBillingUnitPrice());
        updateObj.setActualExposureCount(entity.getActualExposureCount() + exposureCount);
        updateObj.setActualPlayCount(entity.getActualPlayCount() + 1);
        getInjectedMapper().updateById(updateObj);
        return entity.getBillingUnitPrice();
    }

    @Override
    public PageResult<AppAdPlanVO> getMyPage(Integer pageNo, Integer pageSize, Long userId) {
        CoreQueryWrapper<AdPlanDO> wrapper = new CoreQueryWrapper<>();
        wrapper.eq("user_id", userId);
        wrapper.ne("status", AdPlanStatusEnum.PAY.getValue()); // 排除待付款的
        return getPage(pageNo, pageSize, wrapper);
    }

}
